/* Console example — various system commands

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "sdkconfig.h"


#include "internet_common.h"

#include "console_ping.h"

#include "gw-device.h"



#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define WITH_TASKS_INFO 1
#endif

static void register_restore();
static void register_reboot();
static void register_set();
static void register_info();
static void register_debug();
#if WITH_TASKS_INFO
static void register_tasks();
#endif

void register_system()
{
     register_reboot();

#if WITH_TASKS_INFO
    register_tasks();
#endif

   register_restore();
   register_set();
   register_info();
   register_debug();
   register_ping();

}

/** 'restart' command restarts the program */

static int reboot(int argc, char** argv)
{
    ESP_LOGI(__func__, "Rebooting...");
    esp_restart();
}

static void register_reboot()
{
    const esp_console_cmd_t cmd = {
        .command = "reboot",
        .help = "reboot the program",
        .hint = NULL,
        .func = &reboot,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

/** 'tasks' command prints the list of tasks and related information */
#if WITH_TASKS_INFO

static int tasks_info(int argc, char** argv)
{
    const size_t bytes_per_task = 40; /* see vTaskList description */
    char* task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
    if (task_list_buffer == NULL) {
        ESP_LOGE(__func__, "failed to allocate buffer for vTaskList output");
        return 1;
    }
    fputs("Task Name\tStatus\tPrio\tHWM\tTask Number\n", stdout);    
    vTaskList(task_list_buffer);
    fputs(task_list_buffer, stdout);
    free(task_list_buffer);
    return 0;
}

static void register_tasks()
{
    const esp_console_cmd_t cmd = {
        .command = "tasks",
        .help = "Get information about running tasks",
        .hint = NULL,
        .func = &tasks_info,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

#endif // WITH_TASKS_INFO


/** 'restore' command restore factory settings */

static int restore(int argc, char** argv)
{
    printf("restoring factory settings...\n");
    restore_factory_settings();
    printf("restored factory settings\n");
    printf("system will restart after 2 seconds...\n");
    vTaskDelay(1000);
    printf("system will restart after 1 seconds...\n");
    vTaskDelay(1000);
    printf("system restarting\n");
    esp_restart();


    return 0;
}

static void register_restore()
{
    const esp_console_cmd_t cmd = {
        .command = "restore",
        .help = "device store to factory settings",
        .hint = NULL,
        .func = &restore,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

/** 'set' command restore factory settings */

static struct {
    /* Define the allowable command line options, collecting them in argtable[] */
    struct arg_str *dataServer_ip;
    struct arg_str *dataServer_port;
    struct arg_int *rf_freq;
    struct arg_int *sf;
    struct arg_str *connect_sequence;
    struct arg_end *end;
}set_args;

static int set(int argc, char** argv)
{
    /* Parse the command line as defined by argtable[] */
    int nerrors = arg_parse(argc,argv,(void**)&set_args);

    int valid_para_count = 0;
    /* If the parser returned any errors then display them and exit */
    if (nerrors > 0)
    {
        /* Display the error details contained in the arg_end struct.*/
        arg_print_errors(stdout,set_args.end,argv[0]);
        printf("Try '%s --help' for more information.\n",argv[0]);
    }

    // config_para_t temp_para;

    config_para_t* temp_para = (config_para_t*)malloc(sizeof(config_para_t));

    if(temp_para == NULL)
    {
        ESP_LOGE("set","malloc failed");
        ESP_LOGE("set","device set failed");
        return -1;
    }

    printf("current set:\n");
    get_device_settings(temp_para);
    printf_device_settings(temp_para,true,false);

    if(set_args.dataServer_ip->count >0){
        memcpy(temp_para->dataServer_ip,set_args.dataServer_ip->sval[0],strlen(set_args.dataServer_ip->sval[0]));
        temp_para->dataServer_ip[strlen(set_args.dataServer_ip->sval[0])] = 0;
        valid_para_count ++;
    }
    
    if(set_args.dataServer_port->count >0){
        int port_int = 0 ;
        port_int = atoi(set_args.dataServer_port->sval[0]);

        if(port_int <= 65535)
        {
            memcpy(temp_para->dataServer_port,set_args.dataServer_port->sval[0],strlen(set_args.dataServer_port->sval[0]));
            temp_para->dataServer_port[strlen(set_args.dataServer_port->sval[0])] = 0;
            valid_para_count ++;
        }
        else
        {
            printf("Port must <= 65535\n");

            free(temp_para);
            return 1;
        }
        

    }
    
    if(set_args.rf_freq->count >0){
        if(set_args.rf_freq->ival[0] >= 120000000 && set_args.rf_freq->ival[0] <= 1200000000)
        {
            temp_para->rf_freq = set_args.rf_freq->ival[0];
            valid_para_count ++;
        }
        else
        {
            printf("set failed : freq must >= 120000000 &&  <= 1200000000\n");
        
            free(temp_para);
            return 1;
        }
        

    }

    if(set_args.sf->count >0){

        if(set_args.sf->ival[0] >= 7 && set_args.sf->ival[0] <= 12)
        {
            temp_para->sf = set_args.sf->ival[0];        
            valid_para_count ++;
        }
        else
        {
            printf("set failed : sf must >= 7 && <= 12\n");

            free(temp_para);
            return 1;
        }
    }
    
    if(set_args.connect_sequence->count >0){
        if( !strcmp(set_args.connect_sequence->sval[0],"weg") || !strcmp(set_args.connect_sequence->sval[0],"wge") || 
            !strcmp(set_args.connect_sequence->sval[0],"ewg") || !strcmp(set_args.connect_sequence->sval[0],"egw") || 
            !strcmp(set_args.connect_sequence->sval[0],"gwe") || !strcmp(set_args.connect_sequence->sval[0],"gew")  )
        {
            memcpy(temp_para->network_connecting_priority,set_args.connect_sequence->sval[0],strlen(set_args.connect_sequence->sval[0]));
            temp_para->network_connecting_priority[strlen(set_args.connect_sequence->sval[0])] = 0;

            valid_para_count++;
        }
        else 
        {
            printf("set failed : only [weg,wge,ewg,egw,gwe,gew] supported \n");
            
            free(temp_para);
            return 1;

        }
    }

    if(valid_para_count > 0)
    {
        set_device_settings(temp_para);
        save_device_settings();
        ESP_LOGI("set","device set success");
    
        printf("after set:\n");
        printf_device_settings(temp_para,true,false);

        printf("set success,please type 'reboot' to take effect\n");
    }
    else
    {
        printf("set failed,invalid arguments\n");
    }
    

    free(temp_para);

    return 0;
}

static void register_set()
{
    set_args.dataServer_ip      = arg_str0("i",     "data-ip",      "<string>",     "data server domain name of ip,string");
    set_args.dataServer_port    = arg_str0("p",     "data-port",    "<string>",     "data server port,string");
    set_args.rf_freq            = arg_int0("f",     "freq",         "<n>",          "rf rx frequence[hz],uint32_t");
    set_args.sf                 = arg_int0("s",     "sf",           "<n>",          "rf rx sf[7-12], uint8_t");
    set_args.connect_sequence   = arg_str0("c",     "connect-seq",  "<string>",     "w:wifi,e:ethernet,g:2G/3G/4G,eg.[weg] w-first e-second g-third");

    set_args.end = arg_end(2);

    const esp_console_cmd_t cmd = {
        .command = "set",
        .help = "set the device",
        .hint = NULL,
        .func = &set,
        .argtable = &set_args,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

/** 'info' command restore factory settings */

static int info(int argc, char** argv)
{
    config_para_t* temp_para = (config_para_t*)malloc(sizeof(config_para_t));

    if(temp_para == NULL)
    {
        ESP_LOGE("set","malloc failed");
        ESP_LOGE("set","device set failed");
        return -1;
    }
    printf("================Device Info==================\n");
    uint64_t deviceID = getDeviceID();
    printf("\tDeviceID:\t%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n", (uint8_t)(deviceID>>56&0xff),(uint8_t)(deviceID>>48&0xff),(uint8_t)(deviceID>>40&0xff),
                                                                (uint8_t)(deviceID>>32&0xff),(uint8_t)(deviceID>>24&0xff),(uint8_t)(deviceID>>16&0xff),
                                                                (uint8_t)(deviceID>>8&0xff),(uint8_t)(deviceID&0xff));
    printf("\tHW Version:\t%s\n",HW_VERSION);
    printf("\tSW Version:\t%s\n",SW_VERSION);

    switch(get_gwStatus())
    {
        case WORKING:
            printf("\tGW Status:\tWORKING\n");
            break;

        case NO_CONNECTION:
            printf("\tGW Status:\tConntcting\n");
            break;
    }

    get_device_settings(temp_para);
    printf("\ndevice settings:\n");    
    printf_device_settings(temp_para,true,true);

    printf_net_info();

    printf("================END===========================\n");

    free(temp_para);
    return 0;
}

static void register_info()
{
    const esp_console_cmd_t cmd = {
        .command = "info",
        .help = "device infomation(ID & Device settings)",
        .hint = NULL,
        .func = &info,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

/** 'debug' command restore factory settings */

static struct 
{
    struct arg_int *enable;
    struct arg_end *end;
}debug_args;

static int debug(int argc, char** argv)
{
    int nerrors = arg_parse(argc, argv, (void**) &debug_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, debug_args.end, argv[0]);
        return 1;
    }

    if(debug_args.enable->count >0 )
    {
        switch(debug_args.enable->ival[0])
        {
            case 0:
                switch_debug(false);
                printf("disable debug ok\n");
                break;

            case 1:
                switch_debug(true);
                printf("enable debug ok\n");
                break;
            default:
                printf("invalid argument\n");
                break;
        }
    }
    else
    {
        if(get_debug_state())
        {
            printf("current state: enable debug\n");
        }
        else
        {
            printf("current state: disable debug\n");
        }
    }


    return 0;
}

static void register_debug()
{
    debug_args.enable = arg_int0(NULL,NULL,"0/1","0-disable,1-enable");
    debug_args.end = arg_end(3);

    const esp_console_cmd_t cmd = {
        .command = "debug",
        .help = "enable or disable debug printf",
        .hint = NULL,
        .func = &debug,
        .argtable = &debug_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}





